﻿using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Windows.Input;
using System.Linq;
using System.Text;
using MachineQA.DataAccess;
using System.Data.Objects.DataClasses;
using MachineQA.Properties;
using MachineQA.Model;
using System.Collections.ObjectModel;

namespace MachineQA.ViewModel
{
    /// <summary>
    /// UI-friendly wrapper around a Machine object.
    /// </summary>
    public class MachineViewModel : WorkspaceViewModel, IDataErrorInfo
    {
        readonly Machine _machine;
        readonly MachineRepository _machineRepository;
        bool _isSelected = false;
        RelayCommand _saveCommand;
        ObservableCollection<string> _availableLocations; 

        public MachineViewModel(Machine machine, MachineRepository machineRepository)
        {
            _machine = machine;
            _machineRepository = machineRepository;
        }

        #region Presentation properties
        
        public override string DisplayName
        {
            get
            {
                return Resources.MachineViewModel_DisplayName;
            }
        }

        public bool IsSelected
        {
            get { return _isSelected; }
            set
            {
                if (value == _isSelected)
                    return;

                _isSelected = value;

                base.OnPropertyChanged(Resources.IsSelectedPropertyName);
            }
        }

        public ICommand SaveCommand
        {
            get
            {
                if (_saveCommand == null)
                {
                    _saveCommand = new RelayCommand(
                        param => this.Save(),
                        param => this.CanSave
                        );
                }
                return _saveCommand;
            }
        }

        #endregion Presentation properties

        public string Name
        {
            get { return _machine.Name != null ? _machine.Name : ""; }
            set
            {
                if (value == _machine.Name)
                    return;

                _machine.Name = value;
                base.OnPropertyChanged("Name");
            }
        }

        public LocationViewModel Location
        {
            get 
            {
                if (_machine.Location == null)
                    _machine.Location = new Location();
                return new LocationViewModel(_machine.Location, new LocationRepository(ref Constants.entities)); 
            }
            set
            {
                // A well written UI shouldn't ever allow a user to select a non-existing
                // location. This test will catch the attempt to use a incorrect value.
                if ((value == null) || !Constants.entities.Locations.Select((i) => i.Name).ToList().Contains(value.Name))
                    throw new ArgumentException(Resources.LocationDoesntExistError + ": " + value.ToString());

                // Used so that the current machine's location will be initialized if it hasn't been already
                // to avoid a null reference.
                if (value == Location)
                    return;

                this.SetLocation(value);
                //_machine.Location = Constants.entities.Locations.Where((i) => i == value).First();

                base.OnPropertyChanged("Location");
            }
        }

        public List<Energy> Energies
        {
            get { return _machine.Energies.ToList(); }
            set
            {
                if (value == _machine.Energies.ToList())
                    return;

                EntityCollection<Energy> energyCollection = new EntityCollection<Energy>();
                value.ForEach((i) => energyCollection.Add(i));
                _machine.Energies = energyCollection;

                base.OnPropertyChanged("Energies");
            }
        }

        public ObservableCollection<string> AvailableLocations
        {
            get
            {
                if (_availableLocations == null)
                {
                    _availableLocations = new ObservableCollection<string>();
                    foreach (Location loc in new QADatabaseEntities().Locations)
                    {
                        _availableLocations.Add(loc.Name);
                    }
                }
                return _availableLocations;
            }
        }

        #region IDataErrorInfo

        public string Error
        {
            get { return (_machine as IDataErrorInfo).Error; }
        }


        public string this[string propertyName]
        {
            get
            {
                string error = null;

                if (propertyName == "Location")
                {
                    error = this.ValidateLocation();
                }
                return error;
            }
        }

        string ValidateLocation()
        {
            if (this.Location != null)
                return null;

            return Resources.MachineViewModel_Location_Not_Set;
        }
        
        #endregion // IDataErrorInfo

        #region Public Methods

        /// <summary>
        /// Saves the machine to the customer repository.
        /// </summary>
        public void Save()
        {
            if (!this.CanSave)
                throw new InvalidOperationException();

            if (this.IsNewMachine)
                _machineRepository.AddMachine(_machine);

            base.OnPropertyChanged("DisplayName");
        }

        public void SetLocation(LocationViewModel location)
        {
            if (Constants.entities.Locations.Where((i) => i.Name == location.Name).Count() == 0)
                throw new ArgumentException(Resources.MachineViewModel_Location_Doesnt_Exist);
            _machine.Location = Constants.entities.Locations.Where((i) => i.Name == location.Name).First();
        }

        #endregion //Public Methods

        #region Helper Functions

        bool IsNewMachine
        {
            get { return !_machineRepository.ContainsMachine(_machine); }
        }

        bool CanSave
        {
            get { return _machine.IsValid; }
        }

        #endregion // Helper Functions
    }
}

